#include <sdk.h>
#include <wx/progdlg.h>
#include <M_Code_StyledTextCtrl.h>
#include "Astyle.h"
#include "astyleconfigdlg.h"
#include "formattersettings.h"
#include "asstreamiterator.h"

using std::istringstream;
using std::string;

namespace {
  const int idCodeFormatterActiveFile = wxNewId();
  const int idCodeFormatterProject = wxNewId();
}

BEGIN_EVENT_TABLE( AStylePlugin, M_Code_Plugin )
  EVT_MENU( idCodeFormatterActiveFile, AStylePlugin::OnFormatActiveFile )
  EVT_MENU( idCodeFormatterProject, AStylePlugin::OnFormatProject )
END_EVENT_TABLE()

namespace {
  PluginRegistrant<AStylePlugin> reg( _T( "AStyle" ) );
}

AStylePlugin::AStylePlugin() {
  if( !Manager::LoadResource( _T( "Astyle.zip" ) ) ) {
    NotifyMissingFile( _T( "Astyle.zip" ) );
  }
}

AStylePlugin::~AStylePlugin() {
}

cbConfigurationPanel* AStylePlugin::GetConfigurationPanel( wxWindow* parent ) {
  AstyleConfigDlg* dlg = new AstyleConfigDlg( parent );
  return dlg;
}

void AStylePlugin::BuildModuleMenu( const ModuleType type, wxMenu* menu, const FileTreeData* data ) {
  if( !menu || !IsAttached() ) {
    return;
  }
  switch( type ) {
  case mtEditorManager:
    menu->Append( idCodeFormatterActiveFile, _T( "格式化代码" ) );
    break;
  case mtProjectManager:
    if( data ) {
      switch( data->GetKind() ) {
      case FileTreeData::ftdkProject:
        menu->Append( idCodeFormatterProject, _T( "格式化项目" ) );
        break;
      case FileTreeData::ftdkFile:
        menu->Append( idCodeFormatterProject, _T( "格式化文件" ) );
        break;
      default:
        break;
      }
    }
    break;
  default:
    break;
  }
}

void AStylePlugin::OnFormatProject( wxCommandEvent& ) {
  ProjectManager* manager = Manager::Get()->GetProjectManager();
  wxTreeCtrl *tree = manager->GetUI().GetTree();
  if( !tree ) {
    return;
  }
  wxTreeItemId treeItem =  manager->GetUI().GetTreeSelection();
  if( !treeItem.IsOk() ) {
    return;
  }
  const FileTreeData *data = static_cast<FileTreeData*>( tree->GetItemData( treeItem ) );
  if( !data ) {
    return;
  }
  switch( data->GetKind() ) {
  case FileTreeData::ftdkProject: {
    M_Code_Project* prj = data->GetProject();
    wxProgressDialog progressDlg( _T( "请稍等" ), _T( "正在格式化..." ), prj->GetFilesCount(), 0, wxPD_CAN_ABORT | wxPD_AUTO_HIDE | wxPD_SMOOTH );
    progressDlg.Show();
    int i = 0;
    for( FilesList::iterator it = prj->GetFilesList().begin(); it != prj->GetFilesList().end(); ++it ) {
      ProjectFile* pf = *it;
      wxString filename = pf->file.GetFullPath();
      FileType fileType = FileTypeOf( filename );
      if( fileType == ftSource || fileType == ftHeader || fileType == ftTemplateSource ) {
        FormatFile( filename );
        if( false == progressDlg.Update( i++, wxString( _T( "格式化 " ) ) + pf->relativeFilename ) ) {
          break;
        }
      }
    }
  }
  break;
  case FileTreeData::ftdkFile: {
    ProjectFile* f = data->GetProjectFile();
    if( f ) {
      FormatFile( f->file.GetFullPath() );
    }
  }
  break;
  default:
    break;
  }
}

void AStylePlugin::OnFormatActiveFile( wxCommandEvent& ) {
  Execute();
}

int AStylePlugin::Execute() {
  if( !IsAttached() ) {
    return -1;
  }
  M_Code_Editor *ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !ed ) {
    return 0;
  }
  FormatEditor( ed );
  return 0;
}

void AStylePlugin::FormatFile( const wxString &filename ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->IsBuiltinOpen( filename );
  if( ed ) {
    FormatEditor( ed );
  } else {
    ed = Manager::Get()->GetEditorManager()->Open( filename );
    if( ed ) {
      bool changed = FormatEditor( ed );
      if( changed ) {
        //Manager::Get()->GetEditorManager()->Close( filename );

      }
    }
  }
}

static bool BuffersDiffer( const wxString &a, const wxString &b ) {
  const wxChar *aCurrent = a.c_str();
  const wxChar *bCurrent = b.c_str();
  const wxChar * const aEnd = aCurrent + a.Len();
  const wxChar * const bEnd = bCurrent + b.Len();
  while( aCurrent != aEnd && bCurrent != bEnd ) {
    if( *aCurrent != *bCurrent ) {
      while( *aCurrent == ' ' || *aCurrent == '\t' ) {
        if( ++aCurrent == aEnd ) {
          break;
        }
      }
      while( *bCurrent == ' ' || *bCurrent == '\t' ) {
        if( ++bCurrent == bEnd ) {
          break;
        }
      }
      if( ( *aCurrent != '\r' && *aCurrent != '\n' ) || ( *bCurrent != '\r' && *bCurrent != '\n' ) ) {
        return true;
      }
    }
    ++aCurrent;
    ++bCurrent;
  }
  while( aCurrent != aEnd ) {
    if( *aCurrent != ' ' && *aCurrent != '\t' ) {
      return true;
    }
    ++aCurrent;
  }
  while( bCurrent != bEnd ) {
    if( *bCurrent != ' ' && *bCurrent != '\t' ) {
      return true;
    }
    ++bCurrent;
  }
  return false;
}

bool AStylePlugin::FormatEditor( M_Code_Editor *ed ) {
  M_Code_StyledTextCtrl* control = ed->GetControl();
  if( control->GetReadOnly() ) {
    InfoWindow::Tip( _T( "文件为只读" ), _T( "错误" ), 3500 );
    return false;
  }
  bool onlySelected = false;
  wxString edText;
  wxString selText;
  int leftBracesNumber = 0;
  const int pos = control->GetCurrentPos();
  int start = control->GetSelectionStart();
  int end = control->GetSelectionEnd();
  wxString fromWord;
  if( start != end ) {
    onlySelected = true;
    start = control->GetLineIndentPosition( control->LineFromPosition( start ) );
    control->GotoPos( start );
    control->Home();
    start = control->GetCurrentPos();
    control->SetSelectionStart( start );
    end = control->GetLineEndPosition( control->LineFromPosition( end ) );
    control->SetSelectionEnd( end );
    selText = control->GetTextRange( start, end );
    Manager::Get()->GetLogManager()->LogInf( F( _( "----------- %s " ), selText.wx_str() ) );
    wxChar ch;
    int findBracesPos = start;
    while( --findBracesPos > 0 ) {
      ch = control->GetCharAt( findBracesPos );
      int style = control->GetStyleAt( findBracesPos );
      int lexer = control->GetLexer();
      if( lexer == wxSCI_LEX_CPP ) {
        if( style == wxSCI_C_COMMENT || style == wxSCI_C_COMMENTDOC
            || style == wxSCI_C_COMMENTDOCKEYWORD || style == wxSCI_C_COMMENTDOCKEYWORDERROR
            || style == wxSCI_C_COMMENTLINE || style == wxSCI_C_COMMENTLINEDOC
            || style == wxSCI_C_STRING || style == wxSCI_C_CHARACTER ) {
          continue;
        }
      } else if( lexer == wxSCI_LEX_D ) {
        if( style == wxSCI_D_COMMENT || style == wxSCI_D_COMMENTDOC
            || style == wxSCI_D_COMMENTDOCKEYWORD || style == wxSCI_D_COMMENTDOCKEYWORDERROR
            || style == wxSCI_D_COMMENTLINE || style == wxSCI_D_COMMENTLINEDOC
            || style == wxSCI_D_STRING || style == wxSCI_D_CHARACTER ) {
          continue;
        }
      }
      if( ch == _T( '}' ) ) {
        --leftBracesNumber;
      } else if( ch == _T( '{' ) ) {
        ++leftBracesNumber;
      }
    }
    for( int i = leftBracesNumber; i > 0; --i ) {
      edText.Append( _T( "\n" ) );
    }
    edText.Append( selText );
  } else {
    edText = control->GetText();
  }
  wxString formattedText;
  astyle::ASFormatter formatter;
  FormatterSettings settings;
  settings.ApplyTo( formatter );
  const wxString& eolChars = _T( "\n" );
  if( edText.size() && edText.Last() != _T( '\r' ) && edText.Last() != _T( '\n' ) && !onlySelected ) {
    edText += eolChars;
  }
  ASStreamIterator *asi = new ASStreamIterator( ed, edText );
  formatter.init( asi );
  int lineCounter = 0;
  std::vector<int> new_bookmark;
  std::vector<int> ed_breakpoints;
  if( !onlySelected ) {
    if( ed->HasBookmark( 0 ) ) {
      new_bookmark.push_back( 0 );
    }
    if( ed->HasBreakpoint( 0 ) ) {
      ed_breakpoints.push_back( 0 );
    }
  }
  wxSetCursor( *wxHOURGLASS_CURSOR );
  while( formatter.hasMoreLines() ) {
    formattedText << cbC2U( formatter.nextLine().c_str() );
    if( formatter.hasMoreLines() ) {
      formattedText << eolChars;
    }
    ++lineCounter;
    if( asi->FoundBookmark() ) {
      new_bookmark.push_back( lineCounter );
      asi->ClearFoundBookmark();
    }
    if( asi->FoundBreakpoint() ) {
      ed_breakpoints.push_back( lineCounter );
      asi->ClearFoundBreakpoint();
    }
  }
  if( onlySelected && leftBracesNumber > 0 ) {
    while( leftBracesNumber > 0 ) {
      --leftBracesNumber;
      formattedText = formattedText.SubString( formattedText.Find( _T( "{" ) ), formattedText.Length() );
    }
    formattedText = formattedText.SubString( formattedText.Find( eolChars ) + eolChars.Length(), formattedText.Length() );
  }
  bool changed = BuffersDiffer( formattedText, !onlySelected ? edText : selText );
  if( changed ) {
    control->BeginUndoAction();
    if( onlySelected ) {
      control->ReplaceSelection( formattedText );
    } else {
      control->SetText( formattedText );
    }
    for( std::vector<int>::const_iterator i = new_bookmark.begin(); i != new_bookmark.end(); ++i ) {
      ed->ToggleBookmark( *i );
    }
    for( std::vector<int>::const_iterator i = ed_breakpoints.begin(); i != ed_breakpoints.end(); ++i ) {
      ed->ToggleBreakpoint( *i );
    }
    control->EndUndoAction();
    control->GotoPos( pos );
    ed->SetModified( true );
  }
  wxSetCursor( wxNullCursor );
  return changed;
}
